﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace daStaticAnalyser.Analysers
{
    // Объекты и анализаторы
    using daBase.Objects.Safe;
    using daBase.Objects;
    using daBase.Analyser;
    using daSyntaxAnalyser;
    using Settings;
    using daSemanticAnalyser.SemanticTree;
    using daSyntaxAnalyser.SyntaxTree;

    /// <summary>
    /// Статический анализатор
    /// </summary>
    public abstract class BaseStaticAnalyser : Analyser
    {
        // Видимые свойства
        [Category("Общие")]
        [DisplayName("Сообщение")]
        [Description("Сообщение, которое будет показано когда ошибка будет локализована.")]
        public string Message { get { return (settings as AnalyserSettings).Message; } set { (settings as AnalyserSettings).Message = value; Change(); } }

        [Category("Общие")]
        [DisplayName("Уровень")]
        [Description("Уровень важности ошибки.")]
        public int Level { get { return (settings as AnalyserSettings).Level; } set { (settings as AnalyserSettings).Level = value; Change(); } }

        /// <summary>
        /// Сообщение об ошибке
        /// </summary>
        /// <returns>собственно, сообщение об ошибке</returns>
        protected virtual string ErrorMessage()
        {
            return (settings as AnalyserSettings).Message;
        }

        /// <summary>
        /// Добавление ошибки
        /// </summary>
        /// <param name="File">анализируемый файл</param>
        /// <param name="Line">номер строки в файле</param>
        /// <param name="MetaTags">мета теги</param>
        protected void AddError(TokenInfo Token, string Code)
        {
            AnalyserDefaultSettingsAttribute attr = (Attribute.GetCustomAttribute(this.GetType(), typeof(AnalyserDefaultSettingsAttribute)) as AnalyserDefaultSettingsAttribute);
            AddError(ErrorMessage(), (settings as AnalyserSettings).Level, Token.File, Token.Line, Code, Token.MetaTags, attr.Hint);
        }

        /// <summary>
        /// Загрузка анализатора
        /// </summary>
        protected override void Init()
        {
            if (settings == null)
                settings = new AnalyserSettings();
            Attribute attribute = Attribute.GetCustomAttribute(this.GetType(), typeof(AnalyserDefaultSettingsAttribute));
            (settings as AnalyserSettings).Message = (attribute as AnalyserDefaultSettingsAttribute).Message;
            (settings as AnalyserSettings).Level = (attribute as AnalyserDefaultSettingsAttribute).Level;
            base.Init();
        }

        /// <summary>
        /// Собственно анализ
        /// </summary>
        /// <param name="e">набор токенов</param>
        protected virtual void DoAnalyse(SyntaxTreeArgs e) { }

        protected virtual void DoFinishAnalyse(EventArgs e) { }
        /// <summary>
        /// Семантический анализ
        /// </summary>
        /// <param name="e">набор токенов</param>
        protected virtual void DoSemanticAnalyse(SemanticTreeArgs e) { }

        /// <summary>
        /// Условный анализ
        /// </summary>
        /// <param name="sender">инициатор события</param>
        /// <param name="e">узел синтаксического дерева</param>
        public void Analyse(object sender, SyntaxTreeArgs e)
        {
            if (settings.Enabled)
                DoAnalyse(e);
        }

        public void FinishAnalize(object sender, EventArgs e)
        {
            if (settings.Enabled)
                DoFinishAnalyse(e);
        }

        /// <summary>
        /// Условный семантический анализ
        /// </summary>
        /// <param name="sender">инициатор события</param>
        /// <param name="e">корень семантического дерева</param>
        public void SemanticAnalyse(object sender, SemanticTreeArgs e)
        {
            if (settings.Enabled)
                DoSemanticAnalyse(e);
        }

        //Часто используемые методы(размещены здесь пока временно)
        
        /// <summary>
        /// Функция проверяет, является ли текущий узел методом
        /// </summary>
        /// <param name="e">Текущий узел</param>
        /// <param name="Name">Имя предпологаемого метода</param>
        /// <returns>true - текущий узел является методом, false - не является</returns>
        protected bool CheckMethod_GetName(SyntaxTreeArgs e, out String Name)
        {
            Name = "";
            bool rez = e.Element is OuterMethodElement;
            if (rez)
            {
                var OuterMethod = e.Element as OuterMethodElement;
                if (OuterMethod.Header is OuterProcedureHeadElement)
                {
                    Name = (OuterMethod.Header as OuterProcedureHeadElement).Id.Text;
                }
                else
                {
                    Name = (OuterMethod.Header as OuterFunctionHeadElement).Id.Text;
                }
            }
            return rez;
        }

        protected List<String> GlobalVar = new List<string>();//Список глобальных переменных
        protected void GlobalVarFind(ISyntaxTreeElement elt)
        {
            if (elt is UnitElement)
            {
                GlobalVar = new List<string>();
                UnitElement ue = elt as UnitElement;

                foreach (ISyntaxTreeElement intf in ue.Interfaces)
                {
                    var vars = intf as VarDeclarationsElement;
                    var types = intf as TypeDeclarationsElement;
                    if (vars != null)
                    {
                        foreach (var v in vars)
                        {
                            foreach (IdElement id in (v as VarDeclarationElement).Ids) GlobalVar.Add(id.ToString());
                        }
                    }
                    else if (types != null)
                    {
                        foreach (var type in types.Declarations)
                        {
                            var def = type.Definition as FullClassDefinitionElement;
                            if (def == null) continue;
                            foreach (var sec in def.Sections)
                            {
                                var temp = sec as ClassSectionElement;
                                if (temp == null) continue;
                                foreach (var elt1 in temp.Elements)
                                {
                                    vars = elt1 as VarDeclarationsElement;
                                    var var_one = elt1 as VarDeclarationElement;
                                    if (vars != null)
                                    {
                                        foreach (var v in vars)
                                        {
                                            foreach (IdElement id in (v as VarDeclarationElement).Ids) GlobalVar.Add(id.ToString());
                                        }
                                    }
                                    else if (var_one != null)
                                    {
                                        foreach (IdElement id in (var_one as VarDeclarationElement).Ids) GlobalVar.Add(id.ToString());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        protected int CalculateCountOperators(ISyntaxTreeElement Element)
        {
            if (Element == null)
                return 0;
            int result = 0;
            if ((Element is CaseStatementElement) ||
                (Element is TryStatementElement) ||
                (Element is RepeatStatementElement) ||
                (Element is ExceptOnStatementElement) ||
                (Element is ForStatementElement) ||
                (Element is TestStatementElement) ||
                (Element is WhileStatementElement) ||
                (Element is AssignmentElement) ||
                (Element is VariableStatementElement) ||
                (Element is RaiseStatementElement) ||
                (Element is GotoStatementElement))
            {
                result = 1;
            }
            foreach (ISyntaxTreeElement element in Element)
                result += CalculateCountOperators(element);
            return result;
        }
    }
}
